<?php

/**
 * SigmaCMS - Content Management System
 *
 * Copyright (C) 2008 Pavol Biely
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    SigmaCMS
 * @author     Pavol Biely <pavolbiely@gmail.com>
 * @copyright  2008 Pavol Biely
 * @license    http://www.gnu.org/licenses/gpl.txt   GNU General Public License
 * @link       http://pabi3.com/
 * @since      0.1.0
 */

abstract class Media
{
	/**
	 * Root directory
	 *
	 * @var string
	 */
	static private $_root = cfg::DIR_UPLOAD;

	/**
	 * Set other root directory
	 *
	 * @param string $dirname
	 * @return void
	 */
	static public function setRoot($dirname)
	{
		self::$_root = self::clearPath($dirname);
	}

	/**
	 * Set default root directory
	 *
	 * @return void
	 */
	static public function reset()
	{
		self::$_root = cfg::DIR_UPLOAD;
	}

	/**
	 * Remove unnecessary dots and slashes
	 *
	 * @param string $path
	 * @return string
	 */
	static public function clearPath($path)
	{
		// remove double dots
		$path = preg_replace('/\.{2,}/',NULL,$path);
		
		// remove double slashes and change backslashes to normal
		$path = preg_replace('/(\/{2,})|(\\\\+)/','/',$path);

		// remove slashes on the beginning and ending
		$path = trim($path,'/');

		return $path;
	}

	/**
	 * File filter
	 *
	 * @var array
	 */
	static public $filter = array();
	
	/**
	 * Case-sensitive filter
	 *
	 * @var boolean
	 */
	static public $filterCase = false;

	/**
	 * Set filter for files
	 *
	 * @param array $types
	 * @param boolean $caseSensitive
	 * @return void
	 */
	static public function filter(array $types, $caseSensitive = false)
	{
		// check size of characters?
		if ($caseSensitive) {
			self::$filterCase = true;
		}
		
		// filter file types (only alphanumeric characters are allowed)
		$types = array_filter($types,
		create_function(
		'$type',
		'if (preg_match("/^([a-z0-9]+)$/i", $type)) { return true; } else { return false; }')
		);
		
		// set filter
		self::$filter = $types;
	}

	/**
	 * Upload file
	 *
	 * @param array $file
	 * @param string $path
	 * @param string &$errmsg
	 * @param boolean $rewrite
	 * @return boolean
	 */
	static public function upload(array $file = array(), $path = NULL, &$errmsg = NULL, $rewrite = false)
	{
		// event before file upload
		Main::addEvent('preUpload',$file,$path);

		// upload errors
		switch ($file['error']) {
			case UPLOAD_ERR_OK:
				$errmsg = _MEDIA_UPLOAD_ERR_OK;
				break;
			case UPLOAD_ERR_INI_SIZE:
				$errmsg = _MEDIA_UPLOAD_ERR_INI_SIZE;
				return false;
			case UPLOAD_ERR_FORM_SIZE:
				$errmsg = _MEDIA_UPLOAD_ERR_FORM_SIZE;
				return false;
			case UPLOAD_ERR_PARTIAL:
				$errmsg = _MEDIA_UPLOAD_ERR_PARTIAL;
				break;
			case UPLOAD_ERR_NO_FILE:
				$errmsg = _MEDIA_UPLOAD_ERR_NO_FILE;
				return false;
			case UPLOAD_ERR_NO_TMP_DIR:
				$errmsg = _MEDIA_UPLOAD_ERR_NO_TMP_DIR;
				return false;
			case UPLOAD_ERR_CANT_WRITE:
				$errmsg = _MEDIA_UPLOAD_ERR_CANT_WRITE;
				return false;
		}

		// filepath
		$uploadDir = dirname(__FILE__).'/../'.self::$_root.self::clearPath($path).'/';

		// filename
		$fileName  = self::clearPath($file['name']);

		// is filter defined?
		if (count(self::$filter) > 0) {
			// join array; preparation for regular expression
			$filter = implode('|', self::$filter);

			// ignore size of characters (in regular expression)
			$i = 'i';

			// is filter case-sensitive?
			if (self::$filterCase) {
				$i = NULL;
			}

			// file does not match the filter criteria?
			if (!preg_match('/\.('.$filter.')$/'.$i,$fileName)) {
				// copy filter to array
				$msg = self::$filter;

				// add dot before file suffix
				$msg[0] = '.'.$msg[0];

				// explode files by commas and dot before name, if there is more than 1 filetype in the filter
				$msg = implode(', .', $msg);

				// error message
				$errmsg = sprintf(_MEDIA_UPLOAD_FILTER,$msg);

				return false;
			}
		}

		// is upload directory real "directory"?
		if (!is_dir($uploadDir)) {
			$errmsg = _MEDIA_UPLOAD_DIR_EXISTS;
			return false;
		}

		// is there a file with the same name as the upload file?
		if (file_exists($uploadDir.$fileName) && !$rewrite) {
			$errmsg = _MEDIA_UPLOAD_FILE_EXISTS;
			return false;
		}

		// should the file be rewrited?
		if ($rewrite) {
			@unlink($uploadDir.$fileName);
		}

		// save uploaded file
		$upload = @move_uploaded_file($file['tmp_name'],$uploadDir.$fileName);

		// event after file upload
		Main::addEvent('posUpload',$file,$path,$upload);

		// success?
		if ($upload) {
			return true;
		} else {
			$errmsg = _MEDIA_UPLOAD_ERR_UNKNOWN;
		}

		return false;
	}

	/**
	 * Human readable filesize of file
	 *
	 * @param integer $size
	 * @param boolean $space
	 * @param integer $round
	 * @return string
	 */
	static public function filesize($size, $space = true, $round = 2)
	{
		// add space between number and unit?
		if ($space) {
			$space = ' ';
		} else {
			$space = NULL;
		}

		// fix for numbers from 2 to 4GB
		$size = sprintf("%u",$size);

		// if the filesize is 0
		if ($size == 0) return '0'.$space.'B';

		// array of units
		$units = array('B','KB','MB','GB','TB','PB','EB','ZB','YB');

		// formula for filesize
		foreach ($units as $unit) {
			if ($size >= 1024 && $unit != 'YB') {
				$size /= 1024;
			} else {
				if ($round >= 0) {
					return (string) round($size,$round).$space.$unit;
				} else {
					return (string) $size.$space.$unit;
				}
			}
		}
	}

	/**
	 * Create directory
	 *
	 * @param string $path
	 * @return boolean
	 */
	static public function mkdir($path)
	{
		// file to new directory
		$dirname = dirname(__FILE__).'/../'.self::$_root.self::clearPath($path);

		// is the target real directory?
		if (!is_dir($dirname)) {
			// set umask and create directory with CHMOD 777
			$oldumask = umask(0);
			$mkdir = @mkdir($dirname,0777);
			umask($oldumask);
		} else {
			// change CHMOD to 777
			$oldumask = umask(0);
			$mkdir = @chmod($dirname,0777);
			umask($oldumask);
		}

		return $mkdir;
	}

	/**
	 * Delete directory (recursive)
	 *
	 * @param string $path
	 * @param boolean $empty
	 * @return boolean
	 */
	static public function rmdir($path, $empty = false)
	{
		static $first = false;

		// if the directory does not exists - it is deleted
		if (!is_dir($path)) {
			return true;
		}

		// path to new directory (will be created just once)
		if ($first == false) {
			$first   = true;
			$dirname = dirname(__FILE__).'/../'.self::$_root.self::clearPath($path);
		} else {
			$dirname = $path;
		}

		if (substr($dirname,-1) == '/') {
			$dirname = substr($dirname,0,-1);
		}

		if (!file_exists($dirname) || !is_dir($dirname)) {
			return false;
		} else if (is_readable($dirname)) {
			$handle = @opendir($dirname);
			while (false !== ($item = readdir($handle))) {
				if ($item != '.' && $item != '..') {
					$path = $dirname.'/'.$item;
					if (is_dir($path)) {
						self::rmdir($path);
					} else {
						@unlink($path);
					}
				}
			}
			@closedir($handle);
			if ($empty == false) {
				if (!@rmdir($dirname)) {
					return false;
				}
			}
		}

		return true;
	}

	/**
	 * Delete file
	 * 
	 * @param string $path
	 * @return boolean
	 */
	static public function unlink($path)
	{
		// filepath
		$filename = dirname(__FILE__).'/../'.self::$_root.self::clearPath($path);

		// default result is - failure
		$unlink = false;

		if (file_exists($filename)) {
			$unlink = @unlink($filename);
		}

		return $unlink;
	}

	/**
	 * Offer file for download
	 *
	 * @param string $filename
	 * @param string $content
	 * @return void
	 */
	static public function download($filename, $content = NULL)
	{
		if (headers_sent()) return;

		// set MIME type
		if (preg_match('/^(opera|msie)/i',_server('HTTP_USER_AGENT'))) {
			$contentType = 'application/octetstream';
		} else {
			$contentType = 'application/octet-stream';
		}

		// remove unnecessary characters
		$filename = str_replace(array("\n","\r","\""),NULL,$filename);

		// send headers
		header('Content-Type: '.$contentType);
		header('Content-Disposition: attachment; filename="'.$filename.'"');
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
		header('Accept-Ranges: bytes');
		header('Cache-Control: private');
		header('Pragma: private');
		header("Content-Length: ".strlen($content));

		// print file content
		echo $content;
	}
}
